package schedule

import (
	"context"
	"encoding/json"
	"github.com/cloudwego/hertz-examples/bizdemo/hertz_gorm/biz/dal/mysql"
	"github.com/cloudwego/hertz-examples/bizdemo/hertz_gorm/biz/hertz_gen/user_manager"
	"github.com/cloudwego/hertz-examples/bizdemo/hertz_gorm/biz/model"
	"github.com/cloudwego/hertz-examples/bizdemo/hertz_gorm/biz/pack"
	"github.com/cloudwego/hertz-examples/bizdemo/hertz_gorm/biz/repo/cloudapi"
	"github.com/cloudwego/hertz-examples/bizdemo/hertz_gorm/biz/repo/filehandler"
	"github.com/cloudwego/hertz-examples/bizdemo/hertz_gorm/biz/utils"
	"github.com/cloudwego/hertz/pkg/common/hlog"
	"os"
	"os/signal"
	"path"
	"path/filepath"
	"sync"
	"syscall"
	"time"
)

type Daemon struct {
	ctx        context.Context
	signalChan chan os.Signal
	wg         sync.WaitGroup
}

func NewDaemon(ctx context.Context) *Daemon {
	return &Daemon{
		ctx:        ctx,
		signalChan: make(chan os.Signal, 1),
	}
}

// 信号处理
func (d *Daemon) handleSignals() {
	signal.Notify(d.signalChan,
		syscall.SIGTERM, // 终止信号
		syscall.SIGINT,  // 中断信号
		syscall.SIGHUP,  // 重载配置
		syscall.SIGUSR1, // 自定义信号1
		syscall.SIGUSR2, // 自定义信号2
	)

	go func() {
		for sig := range d.signalChan {
			switch sig {
			case syscall.SIGTERM, syscall.SIGINT:
				hlog.CtxInfof(d.ctx, "收到停止信号，准备退出...")
				os.Exit(0)
			case syscall.SIGHUP:
				hlog.CtxInfof(d.ctx, "收到重载配置信号")
				// 实现配置重载逻辑
			case syscall.SIGUSR1:
				hlog.CtxInfof(d.ctx, "收到自定义信号1")
			case syscall.SIGUSR2:
				hlog.CtxInfof(d.ctx, "收到自定义信号2")
			}
		}
	}()
}

// Run 主工作循环
func (d *Daemon) Run() {
	// 检查是否已经是守护进程

	// 信号处理
	d.handleSignals()

	// 主循环
	hlog.CtxInfof(d.ctx, "守护进程启动成功 PID:%v", os.Getpid())
	d.wg.Add(1)
	go d.worker()
	d.wg.Wait()
}

// 示例工作协程
func (d *Daemon) worker() {
	defer d.wg.Done()
	ticker := time.NewTicker(2 * time.Second)
	defer ticker.Stop()
	for {
		select {
		case <-ticker.C:
			// 在此添加业务逻辑
			tasks, err := mysql.GetAllFileTaskByStatus(d.ctx, int8(user_manager.FileTaskStatus_Running))
			if err != nil {
				hlog.CtxInfof(d.ctx, "获取任务列表失败: %v", err)
				continue
			}
			if len(tasks) == 0 {
				continue
			}
			hlog.CtxInfof(d.ctx, "本批获取（%v）个待执行任务", len(tasks))
			// 不同的任务类型发给不同的handler
			for _, task := range tasks {
				handler := NewFileHandler(user_manager.FileTaskType(task.TaskType))
				if handler.method == nil {
					hlog.CtxInfof(d.ctx, "任务类型[%v]未注册处理函数::%v", task.TaskType,
						utils.QuickJSONMarshal(task))
					continue
				}
				if err = handler.Handle(d.ctx, task); err != nil {
					hlog.CtxErrorf(d.ctx, "任务[%v]处理失败: %v", utils.QuickJSONMarshal(task), err)
					task.TaskStatus = int8(user_manager.FileTaskStatus_Failed)
					task.OutputLog = err.Error()
					if err = mysql.DB.Save(task).Error; err != nil {
						hlog.CtxErrorf(d.ctx, "任务[%v]状态更新失败: %v", utils.QuickJSONMarshal(task), err)
					}
					continue
				}
				task.TaskStatus = int8(user_manager.FileTaskStatus_Success)
				if err = mysql.DB.Save(task).Error; err != nil {
					hlog.CtxErrorf(d.ctx, "任务[%v]状态更新失败: %v", utils.QuickJSONMarshal(task), err)
				}
			}
		}
	}
}

type fileHandler struct {
	method fileHandlerFunc
}

func NewFileHandler(taskType user_manager.FileTaskType) fileHandler {
	return fileHandler{
		method: fileHandlerSet[taskType],
	}
}

func (h fileHandler) Handle(ctx context.Context, task *model.FileTask) error {
	hlog.CtxInfof(ctx, "测试libreoffice....")
	hlog.CtxInfof(ctx, "下载测试文件....")
	// 指定要检查的文件夹路径
	tmpPath := "/home/ubuntu/file/tmp/"
	// 检查文件夹是否存在
	if _, err := os.Stat(tmpPath); os.IsNotExist(err) {
		// 如果文件夹不存在，则创建它
		err := os.MkdirAll(tmpPath, 0755)
		if err != nil {
			hlog.CtxErrorf(ctx, "Error creating folder: %v", err)
			return err
		}
		hlog.CtxInfof(ctx, "Folder '%s' created", tmpPath)
	}
	fileTask := pack.FileTask(task)
	var filesPath []string
	for _, file := range fileTask.SourceFile {
		filePath := path.Join(tmpPath, file.FileName)
		if err := cloudapi.NewCosProxy().DownloadFile(ctx, file.FileURL, filePath); err != nil {
			hlog.CtxErrorf(ctx, "Error downloading file: %v", err)
			return err
		}
		filesPath = append(filesPath, filePath)
	}
	targetsUrl, err := h.method(ctx, filesPath)
	if err != nil {
		return err
	}
	hlog.CtxInfof(ctx, "本地处理完成.... %v", utils.QuickJSONMarshal(targetsUrl))
	for _, targetPath := range targetsUrl {
		// 获取文件信息
		targetInfo, err := os.Stat(targetPath)
		if err != nil {
			return err
		}
		targetFile := &user_manager.File{
			FileName: targetInfo.Name(),
			FileSize: targetInfo.Size(),
			FileType: filepath.Ext(targetPath),
		}
		key, err := cloudapi.NewCosProxy().UpLoadFile(context.Background(),
			path.Join("tmp", targetFile.FileName), targetPath)
		// 执行并捕获输出
		if err != nil {
			hlog.CtxErrorf(ctx, "UpLoadFile Error %v", err)
			return err
		}
		hlog.CtxDebugf(ctx, "UpLoadFile %v success", key)
		targetFile.FileType = key
		fileTask.TargetFile = append(fileTask.TargetFile, targetFile)
	}
	task.TargetFiles, _ = json.Marshal(fileTask.TargetFile)
	hlog.CtxInfof(ctx, "处理完成.... %v::%v", utils.QuickJSONMarshal(task), utils.QuickJSONMarshal(fileTask))
	return nil
}

type fileHandlerFunc func(ctx context.Context, filesPath []string) ([]string, error)

var fileHandlerSet = map[user_manager.FileTaskType]fileHandlerFunc{
	user_manager.FileTaskType_PDF_WORD:  pdf2wordHandler,
	user_manager.FileTaskType_PDF_EXCEL: defaultHandler,
	user_manager.FileTaskType_WORD_PDF:  defaultHandler,
	user_manager.FileTaskType_EXCEL_PDF: defaultHandler,
}

func init() {
	registerHandler(user_manager.FileTaskType_PDF_WORD, pdf2wordHandler)
}

func registerHandler(taskType user_manager.FileTaskType, handler fileHandlerFunc) {
	fileHandlerSet[taskType] = handler
}

func defaultHandler(ctx context.Context, _ []string) ([]string, error) {
	hlog.CtxInfof(ctx, "默认处理....")
	return nil, nil
}

func pdf2wordHandler(ctx context.Context, filesPath []string) ([]string, error) {
	// 指定要检查的文件夹路径
	tmpPath := "/home/ubuntu/file/tmp/"
	hlog.CtxInfof(ctx, "转换文件格式....")
	filePath := filesPath[0]
	outputPath, err := filehandler.ConvertFile(context.Background(),
		filePath,
		tmpPath, filehandler.FileFormatDocx, 30)
	if err != nil {
		hlog.CtxErrorf(ctx, "ConvertFile Error %v", err)
		return nil, err
	}
	return []string{outputPath}, nil
}
